-----------------------------------------------------------------------------
-- 定时器类 wd 2020.110.23
-- 提供三种用于定时的方法；
--      延迟执行-delayCall：延迟一定时间执行一次
--      帧频执行-frameCall：每帧执行一次，因为效率问题，此方法建议慎用
--      时间执行-timeCall ：固定一段时间执行一次
-- 定时器采用的时间单位都为毫秒
-- 可通过回调函数return true的方式快速停用计时
-- 注意：这里计算采用了时间差值，并没有提供暂停计时器功能，如有需要向研发部提出需求
-- 示例：
--      utimer.delayCall(function() end)
--      utimer.delayCall(function() end, 1000)
--      utimer.frameCall(function() end, 5)
--      utimer.frameCall(function() end, -1)
--      utimer.timeCall(function() end, 100, 3)
--      utimer.timeCall(function() end, 1000*60, -1)
-----------------------------------------------------------------------------
local utimer = {}

local callList = {}     -- 注册列表
local clear_times = 30  -- 每多少次循环清理一次，默认1秒
local callId = 0        -- 唯一Id计数

-- 打印警告
local warningList = nil
local warningColor = nil
if Application.isEditor then
    warningList = {
        "[utimer] - callback 函数不能为空",
        "[utimer] - 使用timeCall时间间隔不能小于100ms"
    }
    warningColor = {r = 255, g = 255, b = 0}
end
local function warning(index)
    if not warningList then return end

    if warningList[index] then printColor(warningColor, warningList[index]) end
end

-- 缓存系统
local cacheFunctorList = {}
function utimer.GetCacheFunctor(p1,p2,p3,p4,p5,p6,p7)
    local functor = cacheFunctorList[1]
    if functor then
        table.remove(cacheFunctorList,1)
    else
        functor = {}
    end
    functor[1] = p1
    functor[2] = p2
    functor[3] = p3
    functor[4] = p4
    functor[5] = p5
    functor[6] = p6
    functor[7] = p7

    return functor
end
function utimer.CacheFunctor(functor)
    for key, value in pairs(cacheFunctorList) do
        if value[1] == functor[1] then
            return
        end
    end
    functor[1] = nil
    functor[2] = nil
    functor[3] = nil
    functor[4] = nil
    functor[5] = nil
    functor[6] = nil
    functor[7] = nil
    table.insert(cacheFunctorList,functor)
end

--- 延迟调用，只执行一次
-- @param  callback     定时完成后的回调函数
-- @param  delay        延迟执行的时间，单位毫秒，默认为0，相当于下一个帧频调用
function utimer.delayCall(callback, delay,tb)
    if type(callback) ~= "function" then warning(1) return 0 end
    delay = toNumber(delay)/1000

    callId = callId + 1
    callList[#callList+1] = utimer.GetCacheFunctor(callId,callback, Time.realtimeSinceStartup + delay, 1, 1, 0,tb)

    return callId
end

--- 帧频调用，每帧调用一次，Unity的帧频频率 【慎用！！！！】
-- @param  callback     定时完成后的回调函数，如果在回调中返回true，可停用此定时器
-- @param  times        定时次数，默认为1，-1为无限制
function utimer.frameCall(callback, times,tb)
    if type(callback) ~= "function" then warning(1) return 0 end
    times = toNumber(times or 1)
    if times < 0 then times = -1 else times = math.max(1, times) end

    callId = callId + 1
    callList[#callList+1] = utimer.GetCacheFunctor(callId, callback, Time.realtimeSinceStartup, 1, times, 0,tb)

    return callId
end

--- 定时器，时间间隔限制最小单位为100ms，times为0则无限循环
-- @param  callback     定时完成后的回调函数，如果在回调中返回true，可停用此定时器
-- @param  interval     定时间隔，单位毫秒，timeCall限制最小单位为100ms
-- @param  times        定时次数，默认为1，-1为无限制
function utimer.timeCall(callback, interval, times,tb)
    if type(callback) ~= "function" then warning(1) return 0 end
    interval = toNumber(interval)/1000
    if interval < 0.033 then  interval = 0.033 end
    times = toNumber(times or 1)
    if times < 0 then times = -1 else times = math.max(1, times) end

    callId = callId + 1
    callList[#callList+1] = utimer.GetCacheFunctor(callId, callback, Time.realtimeSinceStartup + interval, 1, times, interval,tb)

    return callId
end

--- 取消定时
-- @param  id       调用utimer.delayCall|utimer.frameCall|utimer.timeCall时返回的定时Id
function utimer.remove(id)
    if id == nil then return end
    for i,v in pairs(callList) do
        if v[1] == id then
            table.remove(callList,i)
            utimer.CacheFunctor(v)
            break;
        end
    end
end

--- 取消所有定时
function utimer.removeAll()
    for i,v in pairs(callList) do
        utimer.CacheFunctor(v)
    end
    table.clear(callList)
end

--- 打印当前定时列表
function utimer.dumpDelayCalls()
    print(string.format("list num = %s", #callList))
end

-- 计算用临时变量
local listCount = 0
local callIndex = 0
local callItem = nil
local loopTimes = 0
--[[
    list数组说明
    1 = id
    2 = callback
    3 = next time
    4 = isrunning
    5 = times
    6 = interval
--]]
-- 功能主循环
local traceback = __G__TRACKBACK__ or traceback
local flag = true
local msg = nil
local function loop()
    listCount = #callList
    callIndex = 1
    while callIndex <= listCount do
        callItem = callList[callIndex]
        if callItem then
            if callItem[4] == 1 and callItem[3] <= Time.realtimeSinceStartup then
                flag, msg = xpcall(callItem[2], traceback,callItem[7])
                if callItem[1] then
                    if flag and msg then callItem[4] = 0 end
                    -- if callItem[2]() then callItem[4] = 0 end
                    if callItem[5] ~= -1 then
                        callItem[5] = callItem[5] - 1
                        if callItem[5] <= 0 then
                            callItem[4] = 0
                        end
                    end
                    callItem[3] = callItem[3] + callItem[6]
                end
            end
        end
        callIndex = callIndex + 1
    end

    loopTimes = loopTimes + 1
    -- 清理
    if loopTimes > clear_times then
        loopTimes = 0
        callIndex = listCount
        while callIndex > 0 do
            callItem = callList[callIndex]
            if callItem then
                if callItem[4] == 0 then
                    table.remove(callList, callIndex)
                    utimer.CacheFunctor(callItem)
                end
            end
            callIndex = callIndex - 1
        end
    end
end

UpdateBeat:Add(loop)

---- 全局禁用Update功能，只留timer
--if Application.isEditor then
--    UpdateBeat.Add      = function() error("全局Update功能禁用了，请使用utimer") end
--    LateUpdateBeat.Add  = function() error("全局Update功能禁用了，请使用utimer") end
--    FixedUpdateBeat.Add = function() error("全局Update功能禁用了，请使用utimer") end
--    CoUpdateBeat.Add    = function() error("全局Update功能禁用了，请使用utimer") end
--end

return utimer